#include "Managers/FurnaceRecipesManager.h"
#include "Utils/Utils.h"
#include "Terrain/Terrain.h"

namespace PocketSurvival
{
    FurnaceRecipesManager::FurnaceRecipesManager() {}
    FurnaceRecipesManager::~FurnaceRecipesManager() {}

    void FurnaceRecipesManager::loadGame()
    {
        std::string recipesJsonText;
        if(Utils::LoadTextFile("./content/recipe/FurnaceRecipe.json", recipesJsonText) == true)
        {
            loadFurnaceRecipesSource(nlohmann::json::parse(recipesJsonText));
        }
    }

    void FurnaceRecipesManager::loadFurnaceRecipesSource(const nlohmann::json &jsonData)
    {
        for(const auto &jsonKv : jsonData.items())
        {
            for(const nlohmann::json &recipeJson : jsonKv.value())
            {
                FurnaceRecipe *recipe = new FurnaceRecipe;

                recipe->resultValue = recipeJson["ResultValue"].get<int32_t>();
                recipe->resultCount = recipeJson["ResultCount"].get<uint32_t>();
                recipe->requiredHeatLevel = recipeJson["RequiredHeatLevel"].get<int32_t>();
                recipe->smeltingTimeMultiple = recipeJson["SmeltingTimeMultiple"].get<float>();
                auto remainsValueIt = recipeJson.find("RemainsValue");
                if(remainsValueIt != recipeJson.end())
                {
                    recipe->remainsValue = remainsValueIt->get<int32_t>();
                }
                auto remainsCountIt = recipeJson.find("RemainsCount");
                if(remainsCountIt != recipeJson.end())
                {
                    recipe->remainsCount = remainsCountIt->get<uint32_t>();
                }
                const nlohmann::json &ingredientsJson = recipeJson["Ingredients"];

                uint8_t key = (ingredientsJson.size() & 0xF) << 4;  // 行

                uint8_t columns = 0;
                for(int32_t index = 0; index < ingredientsJson.size(); ++index)
                {
                    std::string text = ingredientsJson[index].get<std::string>();
                    uint8_t textCount = text.length();
                    columns = MathUtils::Max<uint8_t>(columns, textCount);
                    for(int32_t textIndex = 0; textIndex < textCount; ++textIndex)
                    {
                        if(text[textIndex] != ' ')
                        {
                            std::string key(1, text[textIndex]);
                            recipe->ingredients[index * 3 + textIndex] = recipeJson[key][0].get<int32_t>();
                            recipe->ingredientsDataBit[index * 3 + textIndex] = recipeJson[key][1].get<int32_t>();
                        }
                    }
                }
                key |= (columns & 0xF);

                auto it = furnaceRecipesMap.find(key);
                if(it == furnaceRecipesMap.end())
                {
                    furnaceRecipesMap[key] = { recipe };
                }
                else
                {
                    it->second.push_back(recipe);
                }
            }
        }
    }

    void FurnaceRecipesManager::addFurnaceRecipes(FurnaceRecipe *furnaceRecipe)
    {
        int32_t array[9] = { 0 };
        // 整理合成表内容，并获得key
        uint8_t key = ArrangeRecipe(furnaceRecipe->ingredients, array);
        memcpy(furnaceRecipe->ingredients, array, sizeof(furnaceRecipe->ingredients));

        auto it = furnaceRecipesMap.find(key);
        if(it == furnaceRecipesMap.end())
        {
            furnaceRecipesMap[key] = { furnaceRecipe };
        }
        else
        {
            it->second.push_back(furnaceRecipe);
        }
    }

    FurnaceRecipe *FurnaceRecipesManager::findFurnaceRecipes(int32_t *ingredients)
    {
        FurnaceRecipe *craftingRecipe = nullptr;
        int32_t array[9] = { 0 };
        uint8_t key = ArrangeRecipe(ingredients, array);
        auto it = furnaceRecipesMap.find(key);
        if(it != furnaceRecipesMap.end())
        {
            for(FurnaceRecipe *recipe : it->second)
            {
                for(uint8_t index = 0; index < 9; ++index)
                {
                    if((array[index] & recipe->ingredientsDataBit[index]) != recipe->ingredients[index])
                    {
                        goto nextRecipe;
                    }
                }
                craftingRecipe = recipe;
                break;
            nextRecipe:;
            }
        }
        return craftingRecipe;
    }

    uint8_t FurnaceRecipesManager::ArrangeRecipe(int32_t *ingredients, int32_t *outIngredients)
    {
        uint8_t key = 0;
        uint8_t startX = UINT8_MAX;
        uint8_t startY = UINT8_MAX;
        uint8_t endX = 0;
        uint8_t endY = 0;
        for(uint8_t y = 0; y < 3; ++y)
        {
            for(uint8_t x = 0; x < 3; ++x)
            {
                if(ingredients[y * 3 + x] != 0)
                {
                    startX = MathUtils::Min<uint8_t>(x, startX);
                    startY = MathUtils::Min<uint8_t>(y, startY);
                    endX = MathUtils::Max<uint8_t>(x, endX);
                    endY = MathUtils::Max<uint8_t>(y, endY);
                }
            }
        }
        key =  (((endY - startY) & 0xF) + 1) << 4;
        key |= (((endX - startX) & 0xF) + 1);
        for(uint8_t y2 = startY, y = 0; y2 <= endY; ++y2, ++y)
        {
            for(uint8_t x2 = startX, x = 0; x2 <= endX; ++x2, ++x)
            {
                outIngredients[y * 3 + x] = ingredients[y2 * 3 + x2];
            }
        }
        return key;
    }
    
} // namespace PocketSurvival
